/*
 * C5.3.10.c
 *
 *  Created on: 2012/12/11
 *      Author: ming
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "rule_info.h"
#include "core.h"
#include "trace.h"
#include "trace_valuecal.h"

static const PSZ RuleID = "C5.3.10";

static const PSZ MESSAGE_ENG = "Boolean operation with invariant result";
static const PSZ MESSAGE_JPN = "結果不変なブール演算";
static const PSZ MESSAGE_CHS = "结果不变的布尔运算";

static const U2 RANK = MSG_RANK_ERR;

static PRULEOUT pRuleHead;
static PRULEOUT pRuleTail;

static VOID newZeroValue(TRVALUE *stResult, BOOL bFloating) {
	stResult->bFloating = bFloating;
	stResult->ucValueFlag = 0;
	stResult->dbValue = 0;
}

static BOOL isBoolInvariant(TRVALUESCP * valueScp) {
	TRVALUE min = valueScp->min;
	TRVALUE max = valueScp->max;
	TRVALUE zero;
	newZeroValue(&zero, FALSE);
	if (Tr_ValueEqual(&min, &zero) && Tr_ValueEqual(&max, &zero)) {
		return TRUE;
	} else if (Tr_ValueLessThan(&max, &zero)) {
		return TRUE;
	} else if (Tr_ValueLessThan(&zero, &min)) {
		return TRUE;
	} else {
		return FALSE;
	}
}

static BOOL isGreater(TRVALUESCP * valueScp1, TRVALUESCP * valueScp2) {
	TRVALUE min1 = valueScp1->min;
	TRVALUE max2 = valueScp2->max;
	if (Tr_ValueLessThan(&max2, &min1)) {
		return TRUE;
	}
	return FALSE;
}

static BOOL isGreaterEqual(TRVALUESCP * valueScp1, TRVALUESCP * valueScp2) {
	TRVALUE min1 = valueScp1->min;
	TRVALUE max2 = valueScp2->max;
	if (Tr_ValueLessThan(&max2, &min1) || Tr_ValueEqual(&max2, &min1)) {
		return TRUE;
	}
	return FALSE;
}

static BOOL isLess(TRVALUESCP * valueScp1, TRVALUESCP * valueScp2) {
	TRVALUE max1 = valueScp1->max;
	TRVALUE min2 = valueScp2->min;
	if (Tr_ValueLessThan(&max1, &min2)) {
		return TRUE;
	}
	return FALSE;
}

static BOOL isLessEqual(TRVALUESCP * valueScp1, TRVALUESCP * valueScp2) {
	TRVALUE max1 = valueScp1->max;
	TRVALUE min2 = valueScp2->min;
	if (Tr_ValueLessThan(&max1, &min2) || Tr_ValueEqual(&max1, &min2)) {
		return TRUE;
	}
	return FALSE;
}

static BOOL isEqual(TRVALUESCP * valueScp1, TRVALUESCP * valueScp2) {
	TRVALUE min1 = valueScp1->min;
	TRVALUE max1 = valueScp1->max;
	TRVALUE min2 = valueScp2->min;
	TRVALUE max2 = valueScp2->max;
	if (Tr_ValueEqual(&min1, &min2) && Tr_ValueEqual(&max1, &max2)) {
		return TRUE;
	}
	return FALSE;
}

static BOOL isNotEqual(TRVALUESCP * valueScp1, TRVALUESCP * valueScp2) {
	if (isGreater(valueScp1, valueScp2) || isLess(valueScp1, valueScp2)) {
		return TRUE;
	}
	return FALSE;
}

static BOOL isZero(TRVALUESCP * valueScp) {
	TRVALUE min = valueScp->min;
	TRVALUE max = valueScp->max;
	TRVALUE zero;
	newZeroValue(&zero, FALSE);
	if (Tr_ValueEqual(&min, &zero) && Tr_ValueEqual(&max, &zero)) {
		return TRUE;
	}
	return FALSE;
}

static BOOL isNotZero(TRVALUESCP * valueScp) {
	TRVALUE min = valueScp->min;
	TRVALUE max = valueScp->max;
	TRVALUE zero;
	newZeroValue(&zero, FALSE);
	if (Tr_ValueLessThan(&zero, &min) || Tr_ValueLessThan(&max, &zero)) {
		return TRUE;
	}
	return FALSE;
}

static BOOL getCmdValue(AST cmd, BOOL bLeft, TRVALUESCP * valueScp) {
	if (bLeft) {
		cmd = LinkCommandList.getLeftOperand(cmd);
	} else {
		cmd = LinkCommandList.getRightOperand(cmd);
	}
	if (!LinkOperand.isCommandList(cmd)) {
		return FALSE;
	}
	cmd = LinkOperand.getParameter(cmd);
	Tr_ScpGetCmdValue(cmd, valueScp);
	return TRUE;
}

/******************************************************************
 * main logic
 *******************************************************************/
static S4 RuleMain(AST cmdIndex, U4 event) {

	INT result = RULE_RESULT_OK;
	PRULEOUT newNode = NULL;
	PRULEOUT tmpNode = NULL;
	PRULEOUT tmpNode1 = NULL;

	AST codeScp = 0;
	AST cmd = 0;
	TRVALUESCP leftValueScp;
	TRVALUESCP rightValueScp;

	if (!(event & EVENT_CMDLIST_PREV)) {
		return EXIT_SUCCESS;
	}

	cmd = cmdIndex;
	if (LinkCommandList.isBooleanNegation(cmd)) {
		if (getCmdValue(cmd, TRUE, &leftValueScp)) {
			if (isBoolInvariant(&leftValueScp)) {
				result = RULE_RESULT_NG;
			}
		}
	} else if (LinkCommandList.isGreaterThan(cmd)) {
		if (getCmdValue(cmd, TRUE, &leftValueScp)
				&& getCmdValue(cmd, FALSE, &rightValueScp)) {
			if (isGreater(&leftValueScp, &rightValueScp)) {
				result = RULE_RESULT_NG;
			}
		}
	} else if (LinkCommandList.isGreaterThanOrEqual(cmd)) {
		if (getCmdValue(cmd, TRUE, &leftValueScp)
				&& getCmdValue(cmd, FALSE, &rightValueScp)) {
			if (isGreaterEqual(&leftValueScp, &rightValueScp)) {
				result = RULE_RESULT_NG;
			}
		}
	} else if (LinkCommandList.isLessThan(cmd)) {
		if (getCmdValue(cmd, TRUE, &leftValueScp)
				&& getCmdValue(cmd, FALSE, &rightValueScp)) {
			if (isLess(&leftValueScp, &rightValueScp)) {
				result = RULE_RESULT_NG;
			}
		}
	} else if (LinkCommandList.isLessThanOrEqual(cmd)) {
		if (getCmdValue(cmd, TRUE, &leftValueScp)
				&& getCmdValue(cmd, FALSE, &rightValueScp)) {
			if (isLessEqual(&leftValueScp, &rightValueScp)) {
				result = RULE_RESULT_NG;
			}
		}
	} else if (LinkCommandList.isEqualTo(cmd)) {
		if (getCmdValue(cmd, TRUE, &leftValueScp)
				&& getCmdValue(cmd, FALSE, &rightValueScp)) {
			if (isEqual(&leftValueScp, &rightValueScp)) {
				result = RULE_RESULT_NG;
			}
		}
	} else if (LinkCommandList.isNotEqualTo(cmd)) {
		if (getCmdValue(cmd, TRUE, &leftValueScp)
				&& getCmdValue(cmd, FALSE, &rightValueScp)) {
			if (isNotEqual(&leftValueScp, &rightValueScp)) {
				result = RULE_RESULT_NG;
			}
		}
	} else if (LinkCommandList.isLogicalAnd(cmd)) {
		if (getCmdValue(cmd, TRUE, &leftValueScp)
				&& getCmdValue(cmd, FALSE, &rightValueScp)) {
			if (isBoolInvariant(&leftValueScp)
					&& isBoolInvariant(&rightValueScp)) {
				result = RULE_RESULT_NG;
			} else if (isZero(&leftValueScp) || isZero(&rightValueScp)) {
				result = RULE_RESULT_NG;
			}
		}
	} else if (LinkCommandList.isLogicalOr(cmd)) {
		if (getCmdValue(cmd, TRUE, &leftValueScp)
				&& getCmdValue(cmd, FALSE, &rightValueScp)) {
			if (isBoolInvariant(&leftValueScp)
					&& isBoolInvariant(&rightValueScp)) {
				result = RULE_RESULT_NG;
			} else if (isNotZero(&leftValueScp) || isNotZero(&rightValueScp)) {
				result = RULE_RESULT_NG;
			}
		}
	} else {
		return EXIT_SUCCESS;
	}

	if (result == RULE_RESULT_OK) {
		return EXIT_SUCCESS;
	}

	/*re-checked?*/
	tmpNode = pRuleHead;
	while (tmpNode != NULL ) {
		if (tmpNode->indexInfo == cmdIndex) {
			if (result > RULE_RESULT_OK) {
				result = RULE_RESULT_OK;
			} else {
				if (tmpNode == pRuleHead) {
					pRuleHead = pRuleHead->next;
					free(tmpNode);
				} else if (tmpNode == pRuleTail) {
					tmpNode1->next = NULL;
					pRuleTail = tmpNode1;
					free(tmpNode);
				} else {
					tmpNode1->next = tmpNode->next;
					free(tmpNode);
				}
			}
			break;
		}
		tmpNode1 = tmpNode;
		tmpNode = tmpNode->next;
	}

	/*make result struct*/
	if (result > RULE_RESULT_OK) {
		newNode = (PRULEOUT) malloc(sizeof(RULEOUT));
		if (pRuleHead == NULL ) {
			pRuleHead = newNode;
		}

		codeScp = LinkCommandList.getCodeScope(cmdIndex);
		newNode->indexInfo = cmdIndex;
		newNode->fileIndex = CCodeScope.getFileIndex(codeScp);
		newNode->beginLine = CCodeScope.getBeginLine(codeScp);
		newNode->beginCol = CCodeScope.getBeginLineOffset(codeScp);
		newNode->endLine = CCodeScope.getEndLine(codeScp);
		newNode->endCol = CCodeScope.getEndLineOffset(codeScp);
		newNode->ruleID = RuleID;
		newNode->errRank = RANK;

		sprintf(newNode->msgBuf_eng, MESSAGE_ENG);
		sprintf(newNode->msgBuf_chs, MESSAGE_CHS);
		sprintf(newNode->msgBuf_jpn, MESSAGE_JPN);

		newNode->rfileIndex = 0;
		newNode->rlocLine = 0;
		newNode->next = NULL;

		if (pRuleTail != NULL ) {
			pRuleTail->next = newNode;
		}

		pRuleTail = newNode;
	}

	return EXIT_SUCCESS;
}

VOID C5_3_10_register(VOID) {
	PRULEMAN * pCurRule = &stRuleManager;
	PRULEMAN newRuleMan = (PRULEMAN) malloc(sizeof(RULEMAN));
	memset(newRuleMan, 0, sizeof(RULEMAN));
	newRuleMan->ruleID = RuleID;
	newRuleMan->ruleSwitch = OFF;
	newRuleMan->headOut = &pRuleHead;
	newRuleMan->tailOut = &pRuleTail;
	newRuleMan->ruleMain_cmd = RuleMain;
	newRuleMan->ruleMain_ast = NULL;
	newRuleMan->nextRule = NULL;
	if (stRuleManager == NULL ) {
		stRuleManager = newRuleMan;
	} else {
		while ((*pCurRule)->nextRule != NULL ) {
			pCurRule = &((*pCurRule)->nextRule);
		}
		(*pCurRule)->nextRule = newRuleMan;
	}
}

